package lte

import (
	"math"

	"gitee.com/thubcc/mobile/utils"
)

type mseq struct {
	sequenceSeedLen int
	state           int64
	g               func(int64) int64
}

func X1(i int64) *mseq {
	return &mseq{
		SEQUENCE_SEED_LEN, i,
		func(x int64) int64 { return x ^ (x >> 3) }}
}

func X2(i int64) *mseq {
	return &mseq{
		SEQUENCE_SEED_LEN, i,
		func(x int64) int64 { return x ^ (x >> 1) ^ (x >> 2) ^ (x >> 3) }}
}

func (m *mseq) Init() *mseq {
	for i := 0; i < SEQUENCE_NC; i++ {
		m.Step()
	}
	return m
}

func (m *mseq) Value() int64 {
	return m.state
}

func (m *mseq) Step() int64 {
	r := m.state
	f := m.g(m.state)
	f = (f & 1) << (m.sequenceSeedLen - 1)
	m.state = (m.state >> 1) ^ f
	return r
}

type PhyRef struct {
	x1Init int64
	x2Init []int64
}

func LTEPR() PhyRef {
	x1Init := X1(1).Init().Value()
	x2Init := make([]int64, SEQUENCE_SEED_LEN)
	for i := 0; i < SEQUENCE_SEED_LEN; i++ {
		x2Init[i] = X2(1 << i).Init().Value()
	}
	return PhyRef{x1Init, x2Init}
}

func (pr *PhyRef) Seq(seed int64, len int) []int {
	r := make([]int, len)
	x2init := func(seed int64) int64 {
		var s int64 = 0
		for i := 0; i < SEQUENCE_SEED_LEN; i++ {
			if ((seed >> i) & 1) == 1 {
				s ^= pr.x2Init[i]
			}
		}
		return s
	}
	x1 := X1(pr.x1Init)
	x2 := X2(x2init(seed))
	for i := 0; i < len; i++ {
		r[i] = int((x1.Step() ^ x2.Step()) & 1)
	}
	return r
}

type CRS struct {
	Cell
	Pilots [][][]complex128
	Pss    *PSS
	Sss0   *SSS
	Sss5   *SSS
}

func NewCRS(cid, nof_ports, nof_prb int, cp string) CRS {
	r := CRS{
		Cell: Cell{
			ID:      cid,
			NofPrb:  nof_prb,
			CP:      cp,
			NofPort: nof_ports,
		},
		Pilots: make([][][]complex128, 2),
		Pss:    LTEPSS[cid%3],
		Sss0:   NewSSS(cid, 0),
		Sss5:   NewSSS(cid, 5),
	}
	cs_of_sym := refsignal_cs_nof_symbols(2 * nof_ports)
	for p := 0; p < 2; p++ {
		r.Pilots[p] = make([][]complex128, LTE_NOF_SF_X_FRAME)
		for s := 0; s < LTE_NOF_SF_X_FRAME; s++ {
			r.Pilots[p][s] = make([]complex128, 4*cs_of_sym*nof_prb)
		}
	}
	for ns := 0; ns < LTE_NSLOTS_X_FRAME; ns++ {
		for p := 0; p < 2; p++ {
			nsymbols := refsignal_cs_nof_symbols(2*p) / 2
			for l := 0; l < nsymbols; l++ {
				c_init := x2_init(cp, cid, ns, l, p)
				seq := PR.Seq(c_init, 2*2*LTE_MAX_PRB)
				for i := 0; i < 2*nof_prb; i++ {
					idx := LTE_REFSIGNAL_PILOT_IDX(i, (ns%2)*nsymbols+l, nof_prb)
					mp := i + LTE_MAX_PRB - nof_prb
					r.Pilots[p][ns/2][idx] = complex(float64(1-2*seq[2*mp]), float64(1-2*seq[2*mp+1])) / math.Sqrt2
				}
			}
		}
	}
	return r
}

func (self *CRS) cs_put(port_id int) utils.Array3 {
	sf_symbols := utils.NewArray3d(LTE_NOF_SF_X_FRAME, 2*LTE_CP_NSYMB(self.CP), self.NofPrb*LTE_NRE)
	for sf := 0; sf < LTE_NOF_SF_X_FRAME; sf++ {
		for l := 0; l < refsignal_cs_nof_symbols(port_id); l++ {
			nsymbol := refsignal_cs_nsymbol(l, self.CP, port_id)
			fidx := (refsignal_cs_v(port_id, l) + (self.ID % 6)) % 6
			for i := 0; i < 2*self.NofPrb; i++ {
				pidx := LTE_REFSIGNAL_PILOT_IDX(i, l, self.NofPrb)
				sf_symbols.D[sf][nsymbol][fidx] = self.Pilots[port_id/2][sf%10][pidx]
				fidx += LTE_NRE / 2
			}
		}
	}
	return sf_symbols
}

func (self *CRS) cs_tx(port_id int, pss, sss bool) utils.Array2 {
	sf_symbols := self.cs_put(port_id)
	if pss {
		copy(sf_symbols.D[0][LTE_CP_NORM_NSYMB-1][5:5+62], self.Pss.ofd)
		copy(sf_symbols.D[5][LTE_CP_NORM_NSYMB-1][5:5+62], self.Pss.ofd)
	}
	if sss {
		copy(sf_symbols.D[0][LTE_CP_NORM_NSYMB-1][5:5+62], self.Sss0.ofd)
		copy(sf_symbols.D[5][LTE_CP_NORM_NSYMB-1][5:5+62], self.Sss5.ofd)
	}
	return ofdm_tx(sf_symbols, self.NofPrb)
}

func (self *CRS) cs_get(port_id, sf int, sf_symbols utils.Array3, pilots utils.Array2) {
	for l := 0; l < refsignal_cs_nof_symbols(port_id); l++ {
		nsymbol := refsignal_cs_nsymbol(l, self.CP, port_id)
		fidx := (refsignal_cs_v(port_id, l) + (self.ID % 6)) % 6
		for i := 0; i < 2*self.NofPrb; i++ {
			pidx := LTE_REFSIGNAL_PILOT_IDX(i, l, self.NofPrb)
			pilots.D[sf%10][pidx] = sf_symbols.D[sf][nsymbol][fidx]
			fidx += LTE_NRE / 2
		}
	}
}
